#!/bin/sh
# dhclient-script for Linux. Dan Halbert, March, 1997.
# Updated for Linux 2.[12] by Brian J. Murrell, January 1999.
# No guarantees about this. I'm a novice at the details of Linux
# networking.

# Notes:

# 0. This script is based on the netbsd script supplied with dhcp-970306.

# 1. ifconfig down apparently deletes all relevant routes and flushes
# the arp cache, so this doesn't need to be done explicitly.

# 2. The alias address handling here has not been tested AT ALL.
# I'm just going by the doc of modern Linux ip aliasing, which uses
# notations like eth0:0, eth0:1, for each alias.

# 3. I have to calculate the network address, and calculate the broadcast
# address if it is not supplied. This might be much more easily done
# by the dhclient C code, and passed on.

# 4. TIMEOUT not tested. ping has a flag I don't know, and I'm suspicious
# of the $1 in its args.

# 'ip' just looks too weird.  /sbin/ip looks less weird.
ip=/bin/ip
DBROOT=/var/misc/dhcp
ROOTHOOKS=/home/hybroad/bin/dhcphooks
ADDRINFOFILE=$DBROOT/dhclient-$interface.info

echo "interface=$interface" > $ADDRINFOFILE
echo "reason=$reason" >> $ADDRINFOFILE
echo "pid=$pid" >> $ADDRINFOFILE
echo "errcode=$errcode" >> $ADDRINFOFILE

make_addrinfo_conf() {
    if [ x$new_ip6_prefixlen != x ]; then
        #DHCPv6
        if [ x$new_ip6_address != x ]; then 
            echo "ip6_address=$new_ip6_address" >> $ADDRINFOFILE
        fi
        if [ x$new_ip6_prefixlen != x ]; then
            echo "ip6_prefixlen=$new_ip6_prefixlen" >> $ADDRINFOFILE
        fi
        if [ x$new_dhcp6_domain_search != x ]; then
            echo "dhcp6_domain_search=$new_dhcp6_domain_search" >> $ADDRINFOFILE
        fi
        if [ "x${new_dhcp6_name_servers}" != x ] ; then
            echo "dhcp6_name_servers=$new_dhcp6_name_servers" >> $ADDRINFOFILE
            i=0
            for item in $dhcp6_name_servers; do
                echo "dns$i=$item" >>  $ADDRINFOFILE
                i=`expr $i + 1`
            done
        fi
    else
        #DHCPv4
        if [ x$new_routers != x ]; then
            echo "routers=$new_routers" >> $ADDRINFOFILE
            for item in $new_routers; do
                echo "gateway=$item" >>  $ADDRINFOFILE
                break;
            done
        fi
        if [ x$new_subnet_mask != x ]; then
            echo "subnet_mask=$new_subnet_mask" >> $ADDRINFOFILE
        fi
        if [ x$new_broadcast_address != x ]; then
            echo "broadcast_address=$new_broadcast_address" >> $ADDRINFOFILE
        fi
        if [ x$new_ip_address != x ]; then
            echo "ip_address=$new_ip_address" >> $ADDRINFOFILE
        fi
        if [ x"$new_domain_search" != x ]; then
            echo "domain_search=$new_domain_search" >> $ADDRINFOFILE
        fi
        if [ x"$new_domain_name_servers" != x ]; then
            echo "domain_name_servers=$new_domain_name_servers" >> $ADDRINFOFILE
            i=0
            for item in $new_domain_name_servers; do
                echo "dns$i=$item" >>  $ADDRINFOFILE
                i=`expr $i + 1`
            done
        fi
    fi
}

make_resolv_conf() {
    if [ x"$new_domain_name_servers" != x ]; then
        #cat /dev/null > $DBROOT/resolv.conf.dhclient
        touch $DBROOT/resolv.conf.dhclient
        chmod 644 $DBROOT/resolv.conf.dhclient
        if [ x"$new_domain_search" != x ]; then
            echo search $new_domain_search >> $DBROOT/resolv.conf.dhclient
        elif [ x"$new_domain_name" != x ]; then
            # Note that the DHCP 'Domain Name Option' is really just a domain
            # name, and that this practice of using the domain name option as
            # a search path is both nonstandard and deprecated.
            echo search $new_domain_name >> $DBROOT/resolv.conf.dhclient
        fi
        for nameserver in $new_domain_name_servers; do
            echo nameserver $nameserver >> $DBROOT/resolv.conf.dhclient
        done

        mv $DBROOT/resolv.conf.dhclient /var/resolv.conf

    elif [ "x${new_dhcp6_name_servers}" != x ] ; then
        #cat /dev/null > $DBROOT/resolv.conf.dhclient6
        touch $DBROOT/resolv.conf.dhclient6
        chmod 644 $DBROOT/resolv.conf.dhclient6

        if [ "x${new_dhcp6_domain_search}" != x ] ; then
            echo search ${new_dhcp6_domain_search} >> $DBROOT/resolv.conf.dhclient6
        fi
        shopt -s nocasematch
        for nameserver in ${new_dhcp6_name_servers} ; do
            # If the nameserver has a link-local address
            # add a <zone_id> (interface name) to it.
            if  [[ "$nameserver" =~ ^fe80:: ]]
            then
                zone_id="%$interface"
            else
                zone_id=
            fi
            echo nameserver ${nameserver}$zone_id >> $DBROOT/resolv.conf.dhclient6
        done
        shopt -u nocasematch

        mv $DBROOT/resolv.conf.dhclient6 /var/resolv.conf
    fi
}

# Must be used on exit.   Invokes the local dhcp client exit hooks, if any.
exit_with_hooks() {
    exit_status=$1
    if [ -f $ROOTHOOKS/dhclient-exit-hooks ]; then
        . $ROOTHOOKS/dhclient-exit-hooks
    fi
    # probably should do something with exit status of the local script
    exit $exit_status
}

# Invoke the local dhcp client enter hooks, if they exist.
if [ -f $ROOTHOOKS/dhclient-enter-hooks ]; then
    exit_status=0
    . $ROOTHOOKS/dhclient-enter-hooks
    # allow the local script to abort processing of this state
    # local script must set exit_status variable to nonzero.
    if [ $exit_status -ne 0 ]; then
        exit $exit_status
    fi
fi

###
### DHCPv4 Handlers
###

if [ x$new_broadcast_address != x ]; then
    new_broadcast_arg="broadcast $new_broadcast_address"
fi
if [ x$old_broadcast_address != x ]; then
    old_broadcast_arg="broadcast $old_broadcast_address"
fi
if [ x$new_subnet_mask != x ]; then
    new_subnet_arg="netmask $new_subnet_mask"
fi
if [ x$old_subnet_mask != x ]; then
    old_subnet_arg="netmask $old_subnet_mask"
fi
if [ x$alias_subnet_mask != x ]; then
    alias_subnet_arg="netmask $alias_subnet_mask"
fi
if [ x$new_interface_mtu != x ]; then
    mtu_arg="mtu $new_interface_mtu"
fi
if [ x$IF_METRIC != x ]; then
    metric_arg="metric $IF_METRIC"
fi

if [ x$reason = xMEDIUM ]; then
    # Linux doesn't do mediums (ok, ok, media).
    exit_with_hooks 0
fi

if [ x$reason = xPREINIT ]; then
    if [ x$alias_ip_address != x ]; then
        # Bring down alias interface. Its routes will disappear too.
        ifconfig $interface:0- inet 0
    fi
    #ifconfig $interface 0 up
    ifconfig $interface 0.0.0.0 up

    # We need to give the kernel some time to get the interface up.
    sleep 1

    exit_with_hooks 0
fi

if [ x$reason = xARPCHECK ] || [ x$reason = xARPSEND ]; then
    exit_with_hooks 0
fi

if [ x$reason = xBOUND ] || [ x$reason = xRENEW ] || [ x$reason = xREBIND ] || [ x$reason = xREBOOT ]; then
    current_hostname=`hostname`
    if [ x$current_hostname = x ] || [ x$current_hostname = "x(none)" ] || [ x$current_hostname = xlocalhost ] || [ x$current_hostname = x$old_host_name ]; then
        if [ x$new_host_name != x$old_host_name ]; then
            hostname "$new_host_name"
        fi
    fi

    if [ x$old_ip_address != x ] && [ x$alias_ip_address != x ] && [ x$alias_ip_address != x$old_ip_address ]; then
        # Possible new alias. Remove old alias.
        ifconfig $interface:0- inet 0
    fi

    #lidong del for wifi
    #if [ x$old_ip_address != x ] && [ x$old_ip_address != x$new_ip_address ]; then
    #    # IP address changed. Bringing down the interface will delete all routes,
    #    # and clear the ARP cache.
    #    ifconfig $interface inet 0 down 
    #fi

    if [ x$old_ip_address = x ] || [ x$old_ip_address != x$new_ip_address ] || [ x$reason = xBOUND ] || [ x$reason = xREBOOT ]; then
        #check ip confilict
        RESULT=`arping -D -w 1 -s 0.0.0.0 -I $interface $new_ip_address | grep "Received 0" | wc -l`
        if [ "$RESULT" = "0" ]; then
            echo "extend_state=2" >> $ADDRINFOFILE
            exit_with_hooks 2
        fi
        ifconfig $interface inet $new_ip_address $new_subnet_arg $new_broadcast_arg $mtu_arg
        # Add a network route to the computed network address.
        for router in $new_routers; do
            if [ "x$new_subnet_mask" = "x255.255.255.255" ] ; then
                route add -host $router dev $interface
            fi
            route add default gw $router $metric_arg dev $interface
        done
    else
        # we haven't changed the address, have we changed other options
        # that we wish to update?
        if [ x$new_routers != x ] && [ x$new_routers != x$old_routers ] ; then
            # if we've changed routers delete the old and add the new.
            for router in $old_routers; do
                route del default gw $router
            done
            for router in $new_routers; do
                if [ "x$new_subnet_mask" = "x255.255.255.255" ] ; then
                    route add -host $router dev $interface
                fi
                route add default gw $router $metric_arg dev $interface
            done
        fi
    fi
    if [ x$new_ip_address != x$alias_ip_address ] && [ x$alias_ip_address != x ];
    then
        ifconfig $interface:0- inet 0
        ifconfig $interface:0 inet $alias_ip_address $alias_subnet_arg
        route add -host $alias_ip_address $interface:0
    fi
    make_addrinfo_conf
    make_resolv_conf
    exit_with_hooks 0
fi

if [ x$reason = xEXPIRE ] || [ x$reason = xFAIL ] || [ x$reason = xRELEASE ] || [ x$reason = xSTOP ]; then
    if [ x$alias_ip_address != x ]; then
        # Turn off alias interface.
        ifconfig $interface:0- inet 0
    fi

    #lidong del for wifi, if down, need rejoin AP.
    #if [ x$old_ip_address != x ]; then
    #    # Shut down interface, which will delete routes and clear arp cache.
    #    ifconfig $interface inet 0 down 
    #fi

    if [ x$alias_ip_address != x ]; then
        ifconfig $interface:0 inet $alias_ip_address $alias_subnet_arg
        route add -host $alias_ip_address $interface:0
    fi
    exit_with_hooks 0
fi

if [ x$reason = xTIMEOUT ]; then
    if [ x$alias_ip_address != x ]; then
        ifconfig $interface:0- inet 0
    fi
    ifconfig $interface inet $new_ip_address $new_subnet_arg $new_broadcast_arg $mtu_arg
    set $new_routers
    if ping -q -c 1 $1; then
        if [ x$new_ip_address != x$alias_ip_address ] && [ x$alias_ip_address != x ]; then
            ifconfig $interface:0 inet $alias_ip_address $alias_subnet_arg
            route add -host $alias_ip_address dev $interface:0
        fi
        for router in $new_routers; do
            if [ "x$new_subnet_mask" = "x255.255.255.255" ] ; then
                route add -host $router dev $interface
            fi
            route add default gw $router $metric_arg dev $interface
        done
        make_addrinfo_conf
        make_resolv_conf
        exit_with_hooks 0
    fi

    #lidong del for wifi
    # ifconfig $interface inet 0 down 
    exit_with_hooks 1
fi

###
### DHCPv6 Handlers
###

if [ x$reason = xPREINIT6 ] ; then
    # Ensure interface is up.
    ${ip} link set ${interface} up

    # Remove any stale addresses from aborted clients.
    ${ip} -f inet6 addr flush dev ${interface} scope global permanent

    exit_with_hooks 0
fi

if [ x${old_ip6_prefix} != x ] || [ x${new_ip6_prefix} != x ] ; then
    echo Prefix ${reason} old=${old_ip6_prefix} new=${new_ip6_prefix}

    exit_with_hooks 0
fi

if [ x$reason = xBOUND6 ] ; then
    if [ x${new_ip6_address} = x ] || [ x${new_ip6_prefixlen} = x ] ; then
        exit_with_hooks 2;
    fi

    ${ip} -f inet6 addr add ${new_ip6_address}/${new_ip6_prefixlen} \
        dev ${interface} scope global

    # Check for nameserver options.
    make_addrinfo_conf
    make_resolv_conf

    exit_with_hooks 0
fi

if [ x$reason = xRENEW6 ] || [ x$reason = xREBIND6 ] ; then
    if [ x${new_ip6_address} = x ] || [ x${new_ip6_prefixlen} = x ] ; then
        exit_with_hooks 2;
    fi

    ${ip} -f inet6 addr add ${new_ip6_address}/${new_ip6_prefixlen} \
        dev ${interface} scope global

    # Make sure nothing has moved around on us.

    # Nameservers/domains/etc.
    if [ "x${new_dhcp6_name_servers}" != "x${old_dhcp6_name_servers}" ] || [ "x${new_dhcp6_domain_search}" != "x${old_dhcp6_domain_search}" ] ; then
        make_addrinfo_conf
        make_resolv_conf
    fi

    exit_with_hooks 0
fi

if [ x$reason = xDEPREF6 ] ; then
    if [ x${new_ip6_prefixlen} = x ] ; then
        exit_with_hooks 2;
    fi

    ${ip} -f inet6 addr change ${new_ip6_address}/${new_ip6_prefixlen} \
        dev ${interface} scope global preferred_lft 0

    exit_with_hooks 0
fi

if [ x$reason = xEXPIRE6 -o x$reason = xRELEASE6 -o x$reason = xSTOP6 ] ; then
    if [ x${old_ip6_address} = x ] || [ x${old_ip6_prefixlen} = x ] ; then
        exit_with_hooks 2;
    fi

    ${ip} -f inet6 addr del ${old_ip6_address}/${old_ip6_prefixlen} \
        dev ${interface}

    exit_with_hooks 0
fi

exit_with_hooks 0
